<?php
/**
 * Celebrio WinCache storage using winchache extension for IIS server
 *
 * @copyright  Copyright (c) 2012 Celebio Software, s.r.o.
 * @package    Kernel
 */
namespace Celebrio\Caching;

use Nette\Object;
use Nette\Caching\ICacheStorage;
use Nette\Caching\Cache;

/**
 * Celebrio WinCache storage using winchache extension for IIS server
 *
 * @author Martin Novák
 */
class WinCacheStorage extends Object implements ICacheStorage
{
	/**#@+ @internal cache structure */
	const META_CALLBACKS = 'callbacks';
	const META_DATA = 'data';
	const META_DELTA = 'delta';
	/**#@-*/
	
	private $prefix;
	
	/**
	 * Checks if WinCache extension is available.
	 * @return bool
	 */
	public static function isAvailable()
	{
		return extension_loaded("wincache");
	}

	/**
	 * Celebrio WinCache storage using winchache extension for IIS server
	 * 
	 * @param string $prefix key prefix
	 * @throws \NotSupportedException throws if WinCache is not supported
	 */
	public function __construct($prefix = "") {
		if(!self::isAvailable()){
			throw new \NotSupportedException("WinCache extension is not available in PHP.");
		}
		
		$this->prefix = (string) $prefix;
	}
	
	/**
	 * Read from cache.
	 * @param  string key
	 * @return mixed|NULL
	 */
	public function read($key) {
		$this->_normalizeKey($key);
		if(!is_string($key)) throw new \NotSupportedException("Read cahce in Nette supports only string as an argument for read.");
		$meta = wincache_ucache_get($key, $success);
		if(!$success){
			return NULL;
		}
		
		// verify dependencies
		if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) {
			wincache_ucache_delete($key);
			return NULL;
		}
		
		if (!empty($meta[self::META_DELTA])) {
			wincache_ucache_delete($key);
			wincache_ucache_set($key, $meta, $meta[self::META_DELTA] + time());
		}
		
		return $meta[self::META_DATA];
	}

	/**
	 * Writes item into the cache.
	 * @param  string key
	 * @param  mixed  data
	 * @param  array  dependencies
	 * @return void
	 */
	function write($key, $data, array $dependencies){
		if (!empty($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY]) || !empty($dp[Cache::ITEMS])) {
			throw new \NotSupportedException('Tags, priority and dependent items are not supported by WinCacheStorage.');
		}
		
		$this->_normalizeKey($key);

		$meta = array(
			self::META_DATA => $data,
		);

		$expire = 0;
		if (!empty($dp[Cache::EXPIRE])) {
			$expire = (int) $dp[Cache::EXPIRE];
			if (!empty($dp[Cache::SLIDING])) {
				$meta[self::META_DELTA] = $expire; // sliding time
			}
		}

		if (!empty($dp[Cache::CALLBACKS])) {
			$meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS];
		}

		wincache_ucache_set($key, $meta, $expire);
	}

	/**
	 * Removes item from the cache.
	 * @param  string key
	 * @return void
	 */
	function remove($key){
		$this->_normalizeKey($key);
		wincache_ucache_delete($key);
	}

	/**
	 * Removes items from the cache by conditions.
	 * @param  array  conditions
	 * @return void
	 */
	function clean(array $conds){
		if (!empty($conds[Cache::ALL])) {
			wincache_ucache_clear();
		} elseif (isset($conds[Cache::TAGS]) || isset($conds[Cache::PRIORITY])) {
			throw new \NotSupportedException('Tags and priority is not supported by WinCacheStorage.');
		}
	}
	
	private function _normalizeKey(&$key)
	{
		$key = $this->prefix . str_replace("\x00", '~', $key);
	}
	
}